package net.pimpas.persistence.hibernate.serviceprovider;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.pimpas.core.configuration.exception.ConfigurationException;
import net.pimpas.core.configuration.file.FileConfiguration;
import net.pimpas.core.context.FrameworkContext;
import net.pimpas.core.scanner.Scanner;
import net.pimpas.core.scanner.exception.ScannerException;
import net.pimpas.core.serviceprovider.ServiceProviderAnnotation;
import net.pimpas.core.serviceprovider.instancetype.ServiceProviderInstanceType;
import net.pimpas.logging.serviceprovider.LoggingServiceProvider;
import net.pimpas.logging.serviceprovider.LoggingServiceProviderFactoryImpl;
import net.pimpas.persistence.filter.ann.Filter;
import net.pimpas.persistence.filter.ann.FilterEntry;
import net.pimpas.persistence.hibernate.configuration.HibernateConfiguration;
import net.pimpas.persistence.hibernate.scanner.HibernateConfigScanner;
import net.pimpas.persistence.hibernate.scanner.HibernateEntityScanner;
import net.pimpas.persistence.hibernate.scanner.HibernateFilterScanner;
import net.pimpas.persistence.serviceprovider.PersistenceServiceProvider;

import org.hibernate.Session;
import org.hibernate.SessionException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.engine.FilterDefinition;

@ServiceProviderAnnotation(
		instanceType=ServiceProviderInstanceType.SINGLETON,
		serviceProviderType=PersistenceServiceProvider.class
)
public class HibernateServiceProviderImpl implements HibernateServiceProvider {
	private static final LoggingServiceProvider logger = LoggingServiceProviderFactoryImpl.getLoggingServiceProvider(HibernateServiceProviderImpl.class);
	private AnnotationConfiguration configuration;
	private SessionFactory sessionFactory;
	
	public HibernateServiceProviderImpl() {
		
	}
	@SuppressWarnings("unchecked")
	public void config() throws ConfigurationException {
		// get scanner
		Scanner<FileConfiguration> scanner = new HibernateConfigScanner();
		this.setConfiguration(new AnnotationConfiguration());
		try {
			logger.debug("Running HibernateConfigScanner... ");
			scanner.scan();
			HibernateConfiguration config = new HibernateConfiguration(this.getConfiguration());
			config.getFilesConfiguration().addAll(scanner.getResult());
			
			Scanner<Class> entityScanner = new HibernateEntityScanner();
			logger.debug("Running HibernateEntityScanner... ");
			entityScanner.scan();
			List<Class> clazz = entityScanner.getResult();
			logger.debug("Adding entity classes... ");
			for(int i=0;i<clazz.size();i++) {
				logger.debug("[Adding Entity class in Hibernate] => "+clazz.get(i).getName());
				this.getConfiguration().addAnnotatedClass(clazz.get(i));
			}
			
			logger.debug("Finished, all ENTITY classess added!");
			
			logger.debug("Running HibernateFilterScaner... ");
			Scanner<Class> filterScan = new HibernateFilterScanner();
			filterScan.scan();
			clazz = filterScan.getResult();
			logger.debug("Adding Filter classes... ");
			Filter f = null;
			Map m = null;
			for(int i=0;i<clazz.size();i++) {
				logger.debug("[Adding Filter in Hibernate] => "+clazz.get(i).getName());
				f = (Filter) clazz.get(i).getAnnotation(Filter.class);
				m = new HashMap();
				for(FilterEntry entry: f.entries())
					m.put(entry.name(), entry.type());
				this.getConfiguration().addFilterDefinition(new FilterDefinition(f.name(), f.defaultCondition(), m));
			}
			
			logger.debug("Finished, all FILTER classes added!");
			
			logger.debug("Starting HibernateConfiguration... ");
			config.consume();
			this.setSessionFactory(this.getConfiguration().buildSessionFactory());
		} catch (ScannerException e) {
			throw new ConfigurationException(e);
		}
	}
	
	public AnnotationConfiguration getConfiguration() {
		return configuration;
	}
	public void setConfiguration(AnnotationConfiguration configuration) {
		this.configuration = configuration;
	}
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	public Session getSession() throws SessionException {
		SessionFactory factory = this.getSessionFactory();
		Session session = factory.getCurrentSession();
		
		return (session!=null) ? session : factory.openSession();
	}
	public Session getSession(FrameworkContext context) throws SessionException {
		Session s = (Session) context.getSession().getAttribute(Session.class.getSimpleName());
		if(s == null) return null;
		if(!s.isConnected())
			throw new SessionException("Closed Session!");
		
		return s;
	}
	
	public boolean isTerminated() {
		return false;
	}
}